#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include "thread.hpp"
#include "lockGuard.hpp"
#include "log.hpp"

const int g_thread_num = 10;


// 本质是: 生产消费模型
template <class T>
class ThreadPool
{
public:
    pthread_mutex_t *getMutex()
    {
        return &lock;
    }

    bool isEmpty()
    {
        return task_queue_.empty();
    }

    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }

    T getTask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }

private:
    ThreadPool(int thread_num = g_thread_num) : num_(thread_num) // 单例设计：构造函数私有化
    {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);
        for (int i = 1; i <= num_; i++)
        {
            threads_.push_back(new Thread(i, routine, this));
        }
    }
    ThreadPool(const ThreadPool<T> &other) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

public:
    // 多线程使用单例的过程
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        // 双检查模式：可以有效减少未来必定要进行加锁检测的问题（详细请看C++特殊类设计的博文）
        if (nullptr == thread_ptr)
        {
            lockGuard lockguard(&mutex);
            // 说明：若不加上述if判断，未来任何一个线程想获取单例，都必须调用getThreadPool接口。
            // 而此时单例已经创建，不会再次new，那么势必会存在大量的申请和释放锁的行为，这个是无用且浪费资源的;
            if (nullptr == thread_ptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }
        }
        return thread_ptr;
    }

    // 启动线程池：创建一个个线程。
    void run()
    {
        for (auto &iter : threads_)
        {
            iter->start();
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功");
        }
    }

    // 线程池本质也是一个生产消费模型
    // 消费者消费过程：从线程池中取出任务对象并执行业务处理
    static void *routine(void *args)
    {
        ThreadData *td = (ThreadData *)args;
        ThreadPool<T> *tp = (ThreadPool<T> *)td->args_;
        while (true)
        {
            T task;
            { // 任务队列是共享资源，需要加锁[取]、[放]
                lockGuard lockguard(tp->getMutex());
                while (tp->isEmpty())
                    tp->waitCond();
                // 来到这一步，说明当前线程是唯一持有锁的线程，可读取任务
                task = tp->getTask(); // 任务队列是共享的-> 将任务从共享，拿到自己的私有空间
            }
            task(td->name_); // 新线程处理任务：这里传递名字是为了后续演示方便观察是哪个线程执行该任务
        }
    }

    // 生产者生产任务对象到线程池中：存放在任务队列，待消费者使用(网络中即服务端主线程及其新线程)
    void pushTask(const T &task)
    {
        lockGuard lockguard(&lock);
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }

    // 析构函数：需要逐一将生产出的线程捕捉并释放。
    ~ThreadPool()
    {
        for (auto &iter : threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock); // 释放锁
        pthread_cond_destroy(&cond);  // 释放条件变量
    }

private:
    std::vector<Thread *> threads_;
    int num_;
    std::queue<T> task_queue_;

    static ThreadPool<T> *thread_ptr;
    static pthread_mutex_t mutex;

    pthread_mutex_t lock;
    pthread_cond_t cond;
};
template <typename T>
ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr; // 单例：懒汉模式

template <typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;